/***************************************************************************/
/*                                                                         */
/*  t42parse.c                                                             */
/*                                                                         */
/*    Type 42 font parser (body).                                          */
/*                                                                         */
/*  Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by            */
/*  Roberto Alameda.                                                       */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include "t42parse.h"
#include "t42error.h"
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_POSTSCRIPT_AUX_H


/*************************************************************************/
/*                                                                       */
/* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
/* messages during execution.                                            */
/*                                                                       */
#undef  FT_COMPONENT
#define FT_COMPONENT trace_t42


static void
t42_parse_font_matrix(T42_Face face,
                      T42_Loader loader);
static void
t42_parse_encoding(T42_Face face,
                   T42_Loader loader);

static void
t42_parse_charstrings(T42_Face face,
                      T42_Loader loader);

static void
t42_parse_sfnts(T42_Face face,
                T42_Loader loader);


/* as Type42 fonts have no Private dict,         */
/* we set the last argument of T1_FIELD_XXX to 0 */
static const
T1_FieldRec t42_keywords[] = {

    #undef  FT_STRUCTURE
#define FT_STRUCTURE T1_FontInfo
    #undef  T1CODE
#define T1CODE T1_FIELD_LOCATION_FONT_INFO

    T1_FIELD_STRING("version", version, 0)
    T1_FIELD_STRING("Notice", notice, 0)
    T1_FIELD_STRING("FullName", full_name, 0)
    T1_FIELD_STRING("FamilyName", family_name, 0)
    T1_FIELD_STRING("Weight", weight, 0)
    T1_FIELD_NUM("ItalicAngle", italic_angle, 0)
    T1_FIELD_BOOL("isFixedPitch", is_fixed_pitch, 0)
    T1_FIELD_NUM("UnderlinePosition", underline_position, 0)
    T1_FIELD_NUM("UnderlineThickness", underline_thickness, 0)

    #undef  FT_STRUCTURE
#define FT_STRUCTURE PS_FontExtraRec
    #undef  T1CODE
#define T1CODE T1_FIELD_LOCATION_FONT_EXTRA

    T1_FIELD_NUM("FSType", fs_type, 0)

    #undef  FT_STRUCTURE
#define FT_STRUCTURE T1_FontRec
    #undef  T1CODE
#define T1CODE T1_FIELD_LOCATION_FONT_DICT

    T1_FIELD_KEY("FontName", font_name, 0)
    T1_FIELD_NUM("PaintType", paint_type, 0)
    T1_FIELD_NUM("FontType", font_type, 0)
    T1_FIELD_FIXED("StrokeWidth", stroke_width, 0)

    #undef  FT_STRUCTURE
#define FT_STRUCTURE FT_BBox
    #undef  T1CODE
#define T1CODE T1_FIELD_LOCATION_BBOX

    T1_FIELD_BBOX("FontBBox", xMin, 0)

    T1_FIELD_CALLBACK("FontMatrix", t42_parse_font_matrix, 0)
    T1_FIELD_CALLBACK("Encoding", t42_parse_encoding, 0)
    T1_FIELD_CALLBACK("CharStrings", t42_parse_charstrings, 0)
    T1_FIELD_CALLBACK("sfnts", t42_parse_sfnts, 0)

    {
        0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0
    }
};


#define T1_Add_Table(p, i, o, l)  (p)->funcs.add((p), i, o, l)
#define T1_Done_Table(p) \
    do \
    { \
        if ((p)->funcs.done) \
            (p)->funcs.done(p); \
    } while (0)
#define T1_Release_Table(p) \
    do \
    { \
        if ((p)->funcs.release) \
            (p)->funcs.release(p); \
    } while (0)

#define T1_Skip_Spaces(p)    (p)->root.funcs.skip_spaces(&(p)->root)
#define T1_Skip_PS_Token(p)  (p)->root.funcs.skip_PS_token(&(p)->root)

#define T1_ToInt(p) \
    (p)->root.funcs.to_int(&(p)->root)
#define T1_ToBytes(p, b, m, n, d) \
    (p)->root.funcs.to_bytes(&(p)->root, b, m, n, d)

#define T1_ToFixedArray(p, m, f, t) \
    (p)->root.funcs.to_fixed_array(&(p)->root, m, f, t)
#define T1_ToToken(p, t) \
    (p)->root.funcs.to_token(&(p)->root, t)

#define T1_Load_Field(p, f, o, m, pf) \
    (p)->root.funcs.load_field(&(p)->root, f, o, m, pf)
#define T1_Load_Field_Table(p, f, o, m, pf) \
    (p)->root.funcs.load_field_table(&(p)->root, f, o, m, pf)


/********************* Parsing Functions ******************/

FT_LOCAL_DEF(FT_Error)
t42_parser_init(T42_Parser parser,
                FT_Stream stream,
                FT_Memory memory,
                PSAux_Service psaux)
{
    FT_Error error = T42_Err_Ok;
    FT_Long size;


    psaux->ps_parser_funcs->init(&parser->root, 0, 0, memory);

    parser->stream = stream;
    parser->base_len = 0;
    parser->base_dict = 0;
    parser->in_memory = 0;

    /*******************************************************************/
    /*                                                                 */
    /* Here a short summary of what is going on:                       */
    /*                                                                 */
    /*   When creating a new Type 42 parser, we try to locate and load */
    /*   the base dictionary, loading the whole font into memory.      */
    /*                                                                 */
    /*   When `loading' the base dictionary, we only set up pointers   */
    /*   in the case of a memory-based stream.  Otherwise, we allocate */
    /*   and load the base dictionary in it.                           */
    /*                                                                 */
    /*   parser->in_memory is set if we have a memory stream.          */
    /*                                                                 */

    if (FT_STREAM_SEEK(0L) ||
        FT_FRAME_ENTER(17))
        goto Exit;

    if (ft_memcmp(stream->cursor, "%!PS-TrueTypeFont", 17) != 0)
    {
        FT_TRACE2(("not a Type42 font\n"));
        error = T42_Err_Unknown_File_Format;
    }

    FT_FRAME_EXIT();

    if (error || FT_STREAM_SEEK(0))
        goto Exit;

    size = stream->size;

    /* now, try to load `size' bytes of the `base' dictionary we */
    /* found previously                                          */

    /* if it is a memory-based resource, set up pointers */
    if (!stream->read)
    {
        parser->base_dict = (FT_Byte*)stream->base + stream->pos;
        parser->base_len = size;
        parser->in_memory = 1;

        /* check that the `size' field is valid */
        if (FT_STREAM_SKIP(size))
            goto Exit;
    }
    else
    {
        /* read segment in memory */
        if (FT_ALLOC(parser->base_dict, size) ||
            FT_STREAM_READ(parser->base_dict, size))
            goto Exit;

        parser->base_len = size;
    }

    parser->root.base = parser->base_dict;
    parser->root.cursor = parser->base_dict;
    parser->root.limit = parser->root.cursor + parser->base_len;

Exit:
    if (error && !parser->in_memory)
        FT_FREE(parser->base_dict);

    return error;
}


FT_LOCAL_DEF(void)
t42_parser_done(T42_Parser parser)
{
    FT_Memory memory = parser->root.memory;


    /* free the base dictionary only when we have a disk stream */
    if (!parser->in_memory)
        FT_FREE(parser->base_dict);

    parser->root.funcs.done(&parser->root);
}


static int
t42_is_space(FT_Byte c)
{
    return (c == ' ' || c == '\t' ||
            c == '\r' || c == '\n' || c == '\f' ||
            c == '\0');
}


static void
t42_parse_font_matrix(T42_Face face,
                      T42_Loader loader)
{
    T42_Parser parser = &loader->parser;
    FT_Matrix* matrix = &face->type1.font_matrix;
    FT_Vector* offset = &face->type1.font_offset;
    FT_Face root = (FT_Face) & face->root;
    FT_Fixed temp[6];
    FT_Fixed temp_scale;


    (void)T1_ToFixedArray(parser, 6, temp, 3);

    temp_scale = FT_ABS(temp[3]);

    /* Set Units per EM based on FontMatrix values.  We set the value to */
    /* 1000 / temp_scale, because temp_scale was already multiplied by   */
    /* 1000 (in t1_tofixed, from psobjs.c).                              */

    root->units_per_EM = (FT_UShort)(FT_DivFix(1000 * 0x10000L,
                                               temp_scale) >> 16);

    /* we need to scale the values by 1.0/temp_scale */
    if (temp_scale != 0x10000L)
    {
        temp[0] = FT_DivFix(temp[0], temp_scale);
        temp[1] = FT_DivFix(temp[1], temp_scale);
        temp[2] = FT_DivFix(temp[2], temp_scale);
        temp[4] = FT_DivFix(temp[4], temp_scale);
        temp[5] = FT_DivFix(temp[5], temp_scale);
        temp[3] = 0x10000L;
    }

    matrix->xx = temp[0];
    matrix->yx = temp[1];
    matrix->xy = temp[2];
    matrix->yy = temp[3];

    /* note that the offsets must be expressed in integer font units */
    offset->x = temp[4] >> 16;
    offset->y = temp[5] >> 16;
}


static void
t42_parse_encoding(T42_Face face,
                   T42_Loader loader)
{
    T42_Parser parser = &loader->parser;
    FT_Byte* cur;
    FT_Byte* limit = parser->root.limit;

    PSAux_Service psaux = (PSAux_Service)face->psaux;


    T1_Skip_Spaces(parser);
    cur = parser->root.cursor;
    if (cur >= limit)
    {
        FT_ERROR(("t42_parse_encoding: out of bounds\n"));
        parser->root.error = T42_Err_Invalid_File_Format;
        return;
    }

    /* if we have a number or `[', the encoding is an array, */
    /* and we must load it now                               */
    if (ft_isdigit(*cur) || *cur == '[')
    {
        T1_Encoding encode = &face->type1.encoding;
        FT_UInt count, n;
        PS_Table char_table = &loader->encoding_table;
        FT_Memory memory = parser->root.memory;
        FT_Error error;
        FT_Bool only_immediates = 0;


        /* read the number of entries in the encoding; should be 256 */
        if (*cur == '[')
        {
            count = 256;
            only_immediates = 1;
            parser->root.cursor++;
        }
        else
            count = (FT_UInt)T1_ToInt(parser);

        T1_Skip_Spaces(parser);
        if (parser->root.cursor >= limit)
            return;

        /* we use a T1_Table to store our charnames */
        loader->num_chars = encode->num_chars = count;
        if (FT_NEW_ARRAY(encode->char_index, count) ||
            FT_NEW_ARRAY(encode->char_name, count) ||
            FT_SET_ERROR(psaux->ps_table_funcs->init(
                             char_table, count, memory)))
        {
            parser->root.error = error;
            return;
        }

        /* We need to `zero' out encoding_table.elements */
        for (n = 0; n < count; n++)
        {
            char* notdef = (char*)".notdef";


            T1_Add_Table(char_table, n, notdef, 8);
        }

        /* Now we need to read records of the form                */
        /*                                                        */
        /*   ... charcode /charname ...                           */
        /*                                                        */
        /* for each entry in our table.                           */
        /*                                                        */
        /* We simply look for a number followed by an immediate   */
        /* name.  Note that this ignores correctly the sequence   */
        /* that is often seen in type42 fonts:                    */
        /*                                                        */
        /*   0 1 255 { 1 index exch /.notdef put } for dup        */
        /*                                                        */
        /* used to clean the encoding array before anything else. */
        /*                                                        */
        /* Alternatively, if the array is directly given as       */
        /*                                                        */
        /*   /Encoding [ ... ]                                    */
        /*                                                        */
        /* we only read immediates.                               */

        n = 0;
        T1_Skip_Spaces(parser);

        while (parser->root.cursor < limit)
        {
            cur = parser->root.cursor;

            /* we stop when we encounter `def' or `]' */
            if (*cur == 'd' && cur + 3 < limit)
            {
                if (cur[1] == 'e' &&
                    cur[2] == 'f' &&
                    t42_is_space(cur[3]))
                {
                    FT_TRACE6(("encoding end\n"));
                    cur += 3;
                    break;
                }
            }
            if (*cur == ']')
            {
                FT_TRACE6(("encoding end\n"));
                cur++;
                break;
            }

            /* check whether we have found an entry */
            if (ft_isdigit(*cur) || only_immediates)
            {
                FT_Int charcode;


                if (only_immediates)
                    charcode = n;
                else
                {
                    charcode = (FT_Int)T1_ToInt(parser);
                    T1_Skip_Spaces(parser);
                }

                cur = parser->root.cursor;

                if (*cur == '/' && cur + 2 < limit && n < count)
                {
                    FT_PtrDist len;


                    cur++;

                    parser->root.cursor = cur;
                    T1_Skip_PS_Token(parser);
                    if (parser->root.error)
                        return;

                    len = parser->root.cursor - cur;

                    parser->root.error = T1_Add_Table(char_table, charcode,
                                                      cur, len + 1);
                    if (parser->root.error)
                        return;
                    char_table->elements[charcode][len] = '\0';

                    n++;
                }
            }
            else
            {
                T1_Skip_PS_Token(parser);
                if (parser->root.error)
                    return;
            }

            T1_Skip_Spaces(parser);
        }

        face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY;
        parser->root.cursor = cur;
    }

    /* Otherwise, we should have either `StandardEncoding', */
    /* `ExpertEncoding', or `ISOLatin1Encoding'             */
    else
    {
        if (cur + 17 < limit &&
            ft_strncmp((const char*)cur, "StandardEncoding", 16) == 0)
            face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD;

        else if (cur + 15 < limit &&
                 ft_strncmp((const char*)cur, "ExpertEncoding", 14) == 0)
            face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT;

        else if (cur + 18 < limit &&
                 ft_strncmp((const char*)cur, "ISOLatin1Encoding", 17) == 0)
            face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1;

        else
        {
            FT_ERROR(("t42_parse_encoding: invalid token\n"));
            parser->root.error = T42_Err_Invalid_File_Format;
        }
    }
}


typedef enum  T42_Load_Status_
{
    BEFORE_START,
    BEFORE_TABLE_DIR,
    OTHER_TABLES

} T42_Load_Status;


static void
t42_parse_sfnts(T42_Face face,
                T42_Loader loader)
{
    T42_Parser parser = &loader->parser;
    FT_Memory memory = parser->root.memory;
    FT_Byte* cur;
    FT_Byte* limit = parser->root.limit;
    FT_Error error;
    FT_Int num_tables = 0;
    FT_ULong count, ttf_size = 0;

    FT_Long n, string_size, old_string_size, real_size;
    FT_Byte* string_buf = NULL;
    FT_Bool allocated = 0;

    T42_Load_Status status;


    /* The format is                                */
    /*                                              */
    /*   /sfnts [ <hexstring> <hexstring> ... ] def */
    /*                                              */
    /* or                                           */
    /*                                              */
    /*   /sfnts [                                   */
    /*      <num_bin_bytes> RD <binary data>        */
    /*      <num_bin_bytes> RD <binary data>        */
    /*      ...                                     */
    /*   ] def                                      */
    /*                                              */
    /* with exactly one space after the `RD' token. */

    T1_Skip_Spaces(parser);

    if (parser->root.cursor >= limit || *parser->root.cursor++ != '[')
    {
        FT_ERROR(("t42_parse_sfnts: can't find begin of sfnts vector\n"));
        error = T42_Err_Invalid_File_Format;
        goto Fail;
    }

    T1_Skip_Spaces(parser);
    status = BEFORE_START;
    string_size = 0;
    old_string_size = 0;
    count = 0;

    while (parser->root.cursor < limit)
    {
        cur = parser->root.cursor;

        if (*cur == ']')
        {
            parser->root.cursor++;
            goto Exit;
        }

        else if (*cur == '<')
        {
            T1_Skip_PS_Token(parser);
            if (parser->root.error)
                goto Exit;

            /* don't include delimiters */
            string_size = (FT_Long)((parser->root.cursor - cur - 2 + 1) / 2);
            if (FT_REALLOC(string_buf, old_string_size, string_size))
                goto Fail;

            allocated = 1;

            parser->root.cursor = cur;
            (void)T1_ToBytes(parser, string_buf, string_size, &real_size, 1);
            old_string_size = string_size;
            string_size = real_size;
        }

        else if (ft_isdigit(*cur))
        {
            if (allocated)
            {
                FT_ERROR(("t42_parse_sfnts: "
                          "can't handle mixed binary and hex strings\n"));
                error = T42_Err_Invalid_File_Format;
                goto Fail;
            }

            string_size = T1_ToInt(parser);

            T1_Skip_PS_Token(parser);           /* `RD' */
            if (parser->root.error)
                return;

            string_buf = parser->root.cursor + 1; /* one space after `RD' */

            parser->root.cursor += string_size + 1;
            if (parser->root.cursor >= limit)
            {
                FT_ERROR(("t42_parse_sfnts: too many binary data\n"));
                error = T42_Err_Invalid_File_Format;
                goto Fail;
            }
        }

        if (!string_buf)
        {
            FT_ERROR(("t42_parse_sfnts: invalid data in sfnts array\n"));
            error = T42_Err_Invalid_File_Format;
            goto Fail;
        }

        /* A string can have a trailing zero byte for padding.  Ignore it. */
        if (string_buf[string_size - 1] == 0 && (string_size % 2 == 1))
            string_size--;

        if (!string_size)
        {
            FT_ERROR(("t42_parse_sfnts: invalid string\n"));
            error = T42_Err_Invalid_File_Format;
            goto Fail;
        }

        for (n = 0; n < string_size; n++)
        {
            switch (status)
            {
                case BEFORE_START:
                    /* load offset table, 12 bytes */
                    if (count < 12)
                    {
                        face->ttf_data[count++] = string_buf[n];
                        continue;
                    }
                    else
                    {
                        num_tables = 16 * face->ttf_data[4] + face->ttf_data[5];
                        status = BEFORE_TABLE_DIR;
                        ttf_size = 12 + 16 * num_tables;

                        if (FT_REALLOC(face->ttf_data, 12, ttf_size))
                            goto Fail;
                    }
                /* fall through */

                case BEFORE_TABLE_DIR:
                    /* the offset table is read; read the table directory */
                    if (count < ttf_size)
                    {
                        face->ttf_data[count++] = string_buf[n];
                        continue;
                    }
                    else
                    {
                        int i;
                        FT_ULong len;


                        for (i = 0; i < num_tables; i++)
                        {
                            FT_Byte* p = face->ttf_data + 12 + 16 * i + 12;


                            len = FT_PEEK_ULONG(p);

                            /* Pad to a 4-byte boundary length */
                            ttf_size += (len + 3) & ~3;
                        }

                        status = OTHER_TABLES;
                        face->ttf_size = ttf_size;

                        /* there are no more than 256 tables, so no size check here */
                        if (FT_REALLOC(face->ttf_data, 12 + 16 * num_tables,
                                       ttf_size + 1))
                            goto Fail;
                    }
                /* fall through */

                case OTHER_TABLES:
                    /* all other tables are just copied */
                    if (count >= ttf_size)
                    {
                        FT_ERROR(("t42_parse_sfnts: too many binary data\n"));
                        error = T42_Err_Invalid_File_Format;
                        goto Fail;
                    }
                    face->ttf_data[count++] = string_buf[n];
            }
        }

        T1_Skip_Spaces(parser);
    }

    /* if control reaches this point, the format was not valid */
    error = T42_Err_Invalid_File_Format;

Fail:
    parser->root.error = error;

Exit:
    if (allocated)
        FT_FREE(string_buf);
}


static void
t42_parse_charstrings(T42_Face face,
                      T42_Loader loader)
{
    T42_Parser parser = &loader->parser;
    PS_Table code_table = &loader->charstrings;
    PS_Table name_table = &loader->glyph_names;
    PS_Table swap_table = &loader->swap_table;
    FT_Memory memory = parser->root.memory;
    FT_Error error;

    PSAux_Service psaux = (PSAux_Service)face->psaux;

    FT_Byte* cur;
    FT_Byte* limit = parser->root.limit;
    FT_UInt n;
    FT_UInt notdef_index = 0;
    FT_Byte notdef_found = 0;


    T1_Skip_Spaces(parser);

    if (parser->root.cursor >= limit)
    {
        FT_ERROR(("t42_parse_charstrings: out of bounds\n"));
        error = T42_Err_Invalid_File_Format;
        goto Fail;
    }

    if (ft_isdigit(*parser->root.cursor))
    {
        loader->num_glyphs = (FT_UInt)T1_ToInt(parser);
        if (parser->root.error)
            return;
    }
    else if (*parser->root.cursor == '<')
    {
        /* We have `<< ... >>'.  Count the number of `/' in the dictionary */
        /* to get its size.                                                */
        FT_UInt count = 0;


        T1_Skip_PS_Token(parser);
        if (parser->root.error)
            return;
        T1_Skip_Spaces(parser);
        cur = parser->root.cursor;

        while (parser->root.cursor < limit)
        {
            if (*parser->root.cursor == '/')
                count++;
            else if (*parser->root.cursor == '>')
            {
                loader->num_glyphs = count;
                parser->root.cursor = cur;  /* rewind */
                break;
            }
            T1_Skip_PS_Token(parser);
            if (parser->root.error)
                return;
            T1_Skip_Spaces(parser);
        }
    }
    else
    {
        FT_ERROR(("t42_parse_charstrings: invalid token\n"));
        error = T42_Err_Invalid_File_Format;
        goto Fail;
    }

    if (parser->root.cursor >= limit)
    {
        FT_ERROR(("t42_parse_charstrings: out of bounds\n"));
        error = T42_Err_Invalid_File_Format;
        goto Fail;
    }

    /* initialize tables */

    error = psaux->ps_table_funcs->init(code_table,
                                        loader->num_glyphs,
                                        memory);
    if (error)
        goto Fail;

    error = psaux->ps_table_funcs->init(name_table,
                                        loader->num_glyphs,
                                        memory);
    if (error)
        goto Fail;

    /* Initialize table for swapping index notdef_index and */
    /* index 0 names and codes (if necessary).              */

    error = psaux->ps_table_funcs->init(swap_table, 4, memory);
    if (error)
        goto Fail;

    n = 0;

    for (;;)
    {
        /* The format is simple:                   */
        /*   `/glyphname' + index [+ def]          */

        T1_Skip_Spaces(parser);

        cur = parser->root.cursor;
        if (cur >= limit)
            break;

        /* We stop when we find an `end' keyword or '>' */
        if (*cur == 'e' &&
            cur + 3 < limit &&
            cur[1] == 'n' &&
            cur[2] == 'd' &&
            t42_is_space(cur[3]))
            break;
        if (*cur == '>')
            break;

        T1_Skip_PS_Token(parser);
        if (parser->root.error)
            return;

        if (*cur == '/')
        {
            FT_PtrDist len;


            if (cur + 1 >= limit)
            {
                FT_ERROR(("t42_parse_charstrings: out of bounds\n"));
                error = T42_Err_Invalid_File_Format;
                goto Fail;
            }

            cur++;                          /* skip `/' */
            len = parser->root.cursor - cur;

            error = T1_Add_Table(name_table, n, cur, len + 1);
            if (error)
                goto Fail;

            /* add a trailing zero to the name table */
            name_table->elements[n][len] = '\0';

            /* record index of /.notdef */
            if (*cur == '.' &&
                ft_strcmp(".notdef",
                          (const char*)(name_table->elements[n])) == 0)
            {
                notdef_index = n;
                notdef_found = 1;
            }

            T1_Skip_Spaces(parser);

            cur = parser->root.cursor;

            (void)T1_ToInt(parser);
            if (parser->root.cursor >= limit)
            {
                FT_ERROR(("t42_parse_charstrings: out of bounds\n"));
                error = T42_Err_Invalid_File_Format;
                goto Fail;
            }

            len = parser->root.cursor - cur;

            error = T1_Add_Table(code_table, n, cur, len + 1);
            if (error)
                goto Fail;

            code_table->elements[n][len] = '\0';

            n++;
            if (n >= loader->num_glyphs)
                break;
        }
    }

    loader->num_glyphs = n;

    if (!notdef_found)
    {
        FT_ERROR(("t42_parse_charstrings: no /.notdef glyph\n"));
        error = T42_Err_Invalid_File_Format;
        goto Fail;
    }

    /* if /.notdef does not occupy index 0, do our magic. */
    if (ft_strcmp((const char*)".notdef",
                  (const char*)name_table->elements[0]))
    {
        /* Swap glyph in index 0 with /.notdef glyph.  First, add index 0  */
        /* name and code entries to swap_table.  Then place notdef_index   */
        /* name and code entries into swap_table.  Then swap name and code */
        /* entries at indices notdef_index and 0 using values stored in    */
        /* swap_table.                                                     */

        /* Index 0 name */
        error = T1_Add_Table(swap_table, 0,
                             name_table->elements[0],
                             name_table->lengths[0]);
        if (error)
            goto Fail;

        /* Index 0 code */
        error = T1_Add_Table(swap_table, 1,
                             code_table->elements[0],
                             code_table->lengths[0]);
        if (error)
            goto Fail;

        /* Index notdef_index name */
        error = T1_Add_Table(swap_table, 2,
                             name_table->elements[notdef_index],
                             name_table->lengths[notdef_index]);
        if (error)
            goto Fail;

        /* Index notdef_index code */
        error = T1_Add_Table(swap_table, 3,
                             code_table->elements[notdef_index],
                             code_table->lengths[notdef_index]);
        if (error)
            goto Fail;

        error = T1_Add_Table(name_table, notdef_index,
                             swap_table->elements[0],
                             swap_table->lengths[0]);
        if (error)
            goto Fail;

        error = T1_Add_Table(code_table, notdef_index,
                             swap_table->elements[1],
                             swap_table->lengths[1]);
        if (error)
            goto Fail;

        error = T1_Add_Table(name_table, 0,
                             swap_table->elements[2],
                             swap_table->lengths[2]);
        if (error)
            goto Fail;

        error = T1_Add_Table(code_table, 0,
                             swap_table->elements[3],
                             swap_table->lengths[3]);
        if (error)
            goto Fail;

    }

    return;

Fail:
    parser->root.error = error;
}


static FT_Error
t42_load_keyword(T42_Face face,
                 T42_Loader loader,
                 T1_Field field)
{
    FT_Error error;
    void* dummy_object;
    void** objects;
    FT_UInt max_objects = 0;


    /* if the keyword has a dedicated callback, call it */
    if (field->type == T1_FIELD_TYPE_CALLBACK)
    {
        field->reader((FT_Face)face, loader);
        error = loader->parser.root.error;
        goto Exit;
    }

    /* now the keyword is either a simple field or a table of fields; */
    /* we are now going to take care of it                            */

    switch (field->location)
    {
        case T1_FIELD_LOCATION_FONT_INFO:
            dummy_object = &face->type1.font_info;
            break;

        case T1_FIELD_LOCATION_FONT_EXTRA:
            dummy_object = &face->type1.font_extra;
            break;

        case T1_FIELD_LOCATION_BBOX:
            dummy_object = &face->type1.font_bbox;
            break;

        default:
            dummy_object = &face->type1;
    }

    objects = &dummy_object;

    if (field->type == T1_FIELD_TYPE_INTEGER_ARRAY ||
        field->type == T1_FIELD_TYPE_FIXED_ARRAY)
        error = T1_Load_Field_Table(&loader->parser, field,
                                    objects, max_objects, 0);
    else
        error = T1_Load_Field(&loader->parser, field,
                              objects, max_objects, 0);

Exit:
    return error;
}


FT_LOCAL_DEF(FT_Error)
t42_parse_dict(T42_Face face,
               T42_Loader loader,
               FT_Byte * base,
               FT_Long size)
{
    T42_Parser parser = &loader->parser;
    FT_Byte* limit;
    FT_Int n_keywords = (FT_Int)(sizeof(t42_keywords) /
                                 sizeof(t42_keywords[0]));


    parser->root.cursor = base;
    parser->root.limit = base + size;
    parser->root.error = T42_Err_Ok;

    limit = parser->root.limit;

    T1_Skip_Spaces(parser);

    while (parser->root.cursor < limit)
    {
        FT_Byte* cur;


        cur = parser->root.cursor;

        /* look for `FontDirectory' which causes problems for some fonts */
        if (*cur == 'F' && cur + 25 < limit &&
            ft_strncmp((char*)cur, "FontDirectory", 13) == 0)
        {
            FT_Byte* cur2;


            /* skip the `FontDirectory' keyword */
            T1_Skip_PS_Token(parser);
            T1_Skip_Spaces(parser);
            cur = cur2 = parser->root.cursor;

            /* look up the `known' keyword */
            while (cur < limit)
            {
                if (*cur == 'k' && cur + 5 < limit &&
                    ft_strncmp((char*)cur, "known", 5) == 0)
                    break;

                T1_Skip_PS_Token(parser);
                if (parser->root.error)
                    goto Exit;
                T1_Skip_Spaces(parser);
                cur = parser->root.cursor;
            }

            if (cur < limit)
            {
                T1_TokenRec token;


                /* skip the `known' keyword and the token following it */
                T1_Skip_PS_Token(parser);
                T1_ToToken(parser, &token);

                /* if the last token was an array, skip it! */
                if (token.type == T1_TOKEN_TYPE_ARRAY)
                    cur2 = parser->root.cursor;
            }
            parser->root.cursor = cur2;
        }

        /* look for immediates */
        else if (*cur == '/' && cur + 2 < limit)
        {
            FT_PtrDist len;


            cur++;

            parser->root.cursor = cur;
            T1_Skip_PS_Token(parser);
            if (parser->root.error)
                goto Exit;

            len = parser->root.cursor - cur;

            if (len > 0 && len < 22 && parser->root.cursor < limit)
            {
                int i;


                /* now compare the immediate name to the keyword table */

                /* loop through all known keywords */
                for (i = 0; i < n_keywords; i++)
                {
                    T1_Field keyword = (T1_Field) & t42_keywords[i];
                    FT_Byte* name = (FT_Byte*)keyword->ident;


                    if (!name)
                        continue;

                    if (cur[0] == name[0] &&
                        len == (FT_PtrDist)ft_strlen((const char*)name) &&
                        ft_memcmp(cur, name, len) == 0)
                    {
                        /* we found it -- run the parsing callback! */
                        parser->root.error = t42_load_keyword(face,
                                                              loader,
                                                              keyword);
                        if (parser->root.error)
                            return parser->root.error;
                        break;
                    }
                }
            }
        }
        else
        {
            T1_Skip_PS_Token(parser);
            if (parser->root.error)
                goto Exit;
        }

        T1_Skip_Spaces(parser);
    }

Exit:
    return parser->root.error;
}


FT_LOCAL_DEF(void)
t42_loader_init(T42_Loader loader,
                T42_Face face)
{
    FT_UNUSED(face);

    FT_MEM_ZERO(loader, sizeof(*loader));
    loader->num_glyphs = 0;
    loader->num_chars = 0;

    /* initialize the tables -- simply set their `init' field to 0 */
    loader->encoding_table.init = 0;
    loader->charstrings.init = 0;
    loader->glyph_names.init = 0;
}


FT_LOCAL_DEF(void)
t42_loader_done(T42_Loader loader)
{
    T42_Parser parser = &loader->parser;


    /* finalize tables */
    T1_Release_Table(&loader->encoding_table);
    T1_Release_Table(&loader->charstrings);
    T1_Release_Table(&loader->glyph_names);
    T1_Release_Table(&loader->swap_table);

    /* finalize parser */
    t42_parser_done(parser);
}


/* END */